package com.autonavi.yunda.yunji.common.web;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.util.Strings;
import org.jetbrains.annotations.NotNull;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;

/**
 * @author cizhong.wcs
 * @date 2022/1/30 11:31 上午
 */
@Slf4j
public class RequestResponseLoggingFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(@NotNull HttpServletRequest request,
                                    @NotNull HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        if ("/status.taobao".equals(request.getServletPath()) || "/tmd_status".equals(request.getServletPath())) {
            filterChain.doFilter(request, response);
        } else {
            ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response);
            HttpServletRequest wrappedRequest = wrappedRequest(request);
            //该接口干扰日志太多,去除该日志记录
            log.info("[ REQUEST ] " + formatRequest(wrappedRequest));
            filterChain.doFilter(wrappedRequest, wrappedResponse);
            log.info("[ RESPONSE ] " + formatResponse(wrappedResponse));
        }
    }

    protected HttpServletRequest wrappedRequest(HttpServletRequest request) {
        if ("POST".equalsIgnoreCase(request.getMethod())) {
            try {
                return new ReReadableRequestWrapper(request);
            } catch (Exception ex) {
                log.error("wrap repeat readable request fail", ex);
            }
        }
        return request;
    }

    protected String formatRequest(HttpServletRequest request) {
        String remoteAddress = request.getRemoteAddr();
        Integer remotePort = request.getRemotePort();
        String method = request.getMethod();
        String path = request.getServletPath();
        StringBuilder headers = new StringBuilder();

        Collections.list(request.getHeaderNames()).forEach(
                name -> headers.append(name).append(":").append(Collections.list(request.getHeaders(name))).append(",")
        );

        String queryParam = request.getQueryString();
        String body = null;
        if ("POST".equalsIgnoreCase(request.getMethod())) {
            if (request instanceof ReReadableRequestWrapper) {
                try {
                    body = IOUtils.toString(request.getReader()).replaceAll("\\s+", "");
                    if (request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart/form-data")) {
                        body = limitLength(body, 200);
                    }
                } catch (IOException e) {
                    log.error("Failed to get request body. ", e);
                }
            } else {
                body = "[Not ReReadableRequest, can not read]";
            }
        }

        return limitLength(String.format("%s:%s %s %s Headers: { %s } queryParam{ %s }: body: %s",
                remoteAddress,
                remotePort,
                method,
                path,
                headers,
                queryParam,
                body
        ), -1);
    }

    protected String formatResponse(ContentCachingResponseWrapper responseWrapper) throws IOException {

        String body = IOUtils.toString(responseWrapper.getContentInputStream());
        responseWrapper.copyBodyToResponse();

        Integer status = responseWrapper.getStatus();

        StringBuilder headers = new StringBuilder();
        responseWrapper.getHeaderNames().forEach(name -> {
            headers.append(name).append(":").append(responseWrapper.getHeaders(name).toString()).append(",");
        });
        return limitLength(String.format("%s Headers: { %s } body: %s",
                status,
                headers,
                body
        ), 10000);
    }

    //todo 后续增加长度限制
    protected String limitLength(String str, int maxLength) {
        if (maxLength >= 0) {
            int length = str.length();
            if (length > maxLength) {
                return Strings.left(str, maxLength) + " ... [ " + (length - maxLength) + " characters are omitted ]";
            }
        }
        return str;
    }
}
